﻿using System;
using System.Collections.Generic;
using System.Linq;
using Model.Repositories;
using DomenModel;
using System.Data.Entity;
using System.Linq.Expressions;
using Model.Repositories.Interfaces;
using System.Data;
using System.Security.Cryptography;
using System.Text;

namespace Infrastructure.Repositories
{
    public class UserRepo : IUserCrud
    {

        /// <summary>
        /// Gets the User by id.
        /// </summary>
        /// <param name="Id">The id.</param>
        /// <returns></returns>
        public User GetById(long Id)
        {
            using (GeneralContext _db = new GeneralContext())
            {

                User userById = _db.Users.FirstOrDefault(p => p.UserId == Id && p.IsActive == true);
                return userById;
            }
           
        }

        /// <summary>
        /// Gets the User by login.
        /// </summary>
        /// <param name="LoginName">Name of the login.</param>
        /// <returns></returns>
        public User GetByLogin(string LoginName)
        {
            using (GeneralContext _db = new GeneralContext())
            { 

                    User userByLogin = _db.Users.FirstOrDefault(p => p.LoginName == LoginName && p.IsActive == true);
                    return userByLogin;


            }

        }

        /// <summary>
        /// Gets the role of user by login.
        /// </summary>
        /// <param name="LoginName">Name of the login.</param>
        /// <returns></returns>
        public string GetRoleOfUser(string LoginName)
        {
            using (GeneralContext _db = new GeneralContext())
            {

                    User userByLogin = _db.Users.FirstOrDefault(p => p.LoginName == LoginName && p.IsActive == true);
                    return userByLogin.Role.Name;

            }

        }

        /// <summary>
        /// Gets the User by email.
        /// </summary>
        /// <param name="email">The email.</param>
        /// <returns></returns>
        public User GetByEmail(string email)
        {
            using (GeneralContext _db = new GeneralContext())
            {

                    User userByEmail = _db.Users.FirstOrDefault(p => p.Email == email && p.IsActive == true );
                    return userByEmail;

            }

        }

        /// <summary>
        /// Saves the User to database.
        /// </summary>
        /// <param name="user">The user.</param>
        public void Save(User user)
        {
            using (GeneralContext _db = new GeneralContext())
            {
                var salt = CreateSalt();
                user.Salt = salt;
                user.Password = CreateHash(salt, user.Password);
                _db.Users.Add(user);
                _db.SaveChanges();
            }
        }

        /// <summary>
        /// Updates the User.
        /// </summary>
        /// <param name="userUpdate">The user for update.</param>
        public void Update(User userUpdate)
        {
            using (GeneralContext _db = new GeneralContext())
            {
                User userByLogin = _db.Users.FirstOrDefault(p => p.LoginName == userUpdate.LoginName && p.IsActive == true);
                userByLogin.LoginName = userUpdate.LoginName;
                userByLogin.FirstName = userUpdate.FirstName;
                userByLogin.LastName = userUpdate.LastName;
                userByLogin.Password = userUpdate.Password;
                var salt = CreateSalt();
                userByLogin.Salt = salt;
                userByLogin.Password = CreateHash(salt, userByLogin.Password);
                userByLogin.Email = userUpdate.Email;
                userByLogin.Region = userUpdate.Region;
                userByLogin.Balance = userUpdate.Balance;
                userByLogin.RoleId = userUpdate.RoleId;
                userByLogin.StatusId = userUpdate.StatusId;
                _db.Entry(userByLogin).State = EntityState.Modified;
                _db.SaveChanges();
            }
        }

        /// <summary>
        /// Deletes the User by id from database.
        /// </summary>
        /// <param name="Id">The id.</param>
        public void Delete(long Id)
        {
            using (GeneralContext _db = new GeneralContext())
            {
                User userForRemove = _db.Users.FirstOrDefault(p => p.UserId == Id);
                _db.Users.Remove(userForRemove); 
                _db.SaveChanges();
            }
            
        }

        /// <summary>
        /// Deletes the User by login name from database.
        /// </summary>
        /// <param name="loginName">Name of the login.</param>
        public void Delete(string loginName)
        {
            using (GeneralContext _db = new GeneralContext())
            {
                User userForRemove = _db.Users.FirstOrDefault(p => p.LoginName == loginName);
                userForRemove.IsActive = false;
                _db.Entry(userForRemove).State = EntityState.Modified;
                _db.SaveChanges();
            }

        }

        /// <summary>
        /// Gets the collection of User.
        /// </summary>
        /// <returns></returns>
        public List<User> GetCollection()
        {
            using (GeneralContext _db = new GeneralContext())
            {
                var allUsers = _db.Users.ToList();
                return allUsers;
            }
        }

        /// <summary>
        /// Gets the list of User for pagination by condition.
        /// </summary>
        /// <param name="page">The page.</param>
        /// <param name="itemsOnPage">The items on page.</param>
        /// <param name="predicate">The predicate.</param>
        /// <param name="descending">if set to <c>true</c> [descending].</param>
        /// <returns></returns>
        public List<User> GetListForPagination(int page, int itemsOnPage, Expression<Func<User, string>> predicate, bool descending)
       {
          using (GeneralContext _db = new GeneralContext())
          {

                  if (descending == true)
                  {
                      var listForPagination = _db.Users.Include("Role")
                          .OrderByDescending(predicate)
                          .Where(p => p.IsActive == true)
                          .Skip((page - 1) * itemsOnPage)
                          .Take(itemsOnPage)
                          .ToList();

                      return listForPagination;
                  }
                  else
                  {
                      var listForPagination = _db.Users.Include("Role")
                          .OrderBy(predicate)
                          .Where(p => p.IsActive == true)
                          .Skip((page - 1) * itemsOnPage)
                          .Take(itemsOnPage)
                          .ToList();

                      return listForPagination;
                  }

          }
       }

        /// <summary>
        /// Gets the list of User for pagination with filter.
        /// </summary>
        /// <param name="page">The page.</param>
        /// <param name="itemsOnPage">The items on page.</param>
        /// <param name="predicateListSelectedField">The predicate list selected field.</param>
        /// <param name="predicateSortBy">The predicate sort by.</param>
        /// <param name="descending">if set to <c>true</c> [descending].</param>
        /// <returns></returns>
        public List<User> GetListForPaginationWithFilter(int page, int itemsOnPage, Expression<Func<User, bool>> predicateListSelectedField, Expression<Func<User, string>> predicateSortBy, bool descending)
        {
            using (GeneralContext _db = new GeneralContext())
            {

                    if (descending == true)
                    {
                        var listForPagination = _db.Users.Include("Role")
                            .OrderByDescending(predicateSortBy)
                            .Where(predicateListSelectedField.Compile())
                            .Where(p => p.IsActive == true)
                            .Skip((page - 1) * itemsOnPage)
                            .Take(itemsOnPage)
                            .ToList();

                        return listForPagination;
                    }
                    else
                    {
                        var listForPagination = _db.Users.Include("Role")
                            .OrderBy(predicateSortBy)
                            .Where(predicateListSelectedField.Compile())
                            .Where(p => p.IsActive == true)
                            .Skip((page - 1) * itemsOnPage)
                            .Take(itemsOnPage)
                            .ToList();

                        return listForPagination;
                    }
              
            }
        }

        /// <summary>
        /// Gets the count of user.
        /// </summary>
        /// <returns></returns>
        public long GetCountOfUser()
        {
            using (GeneralContext _db = new GeneralContext())
            {
                long _i = _db.Users.Where(p => p.IsActive == true).Count();
                return _i;
            }

        }

        /// <summary>
        /// Gets the count of user by predicate.
        /// </summary>
        /// <param name="predicate">The predicate.</param>
        /// <returns></returns>
        public long GetCountOfUser(Expression<Func<User, bool>> predicate)
        {
            using (GeneralContext _db = new GeneralContext())
            {
                long _i = _db.Users.Where(predicate).Where(p=>p.IsActive == true).Count();
                return _i;
            }
        }

        /// <summary>
        /// Creates the salt.
        /// </summary>
        /// <returns></returns>
        private string CreateSalt()
        {
            RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
            byte[] salt = new byte[32];
            rng.GetBytes(salt);
            return Convert.ToBase64String(salt);
        }

        /// <summary>
        /// Creates the hash for User.
        /// </summary>
        /// <param name="salt">The salt.</param>
        /// <param name="password">The password.</param>
        /// <returns></returns>
        private string CreateHash(string salt, string password)
        {
            HashAlgorithm hashAl = HashAlgorithm.Create("MD5");
            string passwordAndSalt = String.Concat(password, salt);
            byte[] passwordAndSaltInBytes = Encoding.Unicode.GetBytes(passwordAndSalt);
            byte[] hashedPasswordAndSalt = hashAl.ComputeHash(passwordAndSaltInBytes);
            return Convert.ToBase64String(hashedPasswordAndSalt);
        }

        /// <summary>
        /// Checks the hash password for User.
        /// </summary>
        /// <param name="username">The username.</param>
        /// <param name="password">The password.</param>
        /// <returns></returns>
        public bool CheckHashPassword(string username, string password)
        {
            bool res = false;
            User user = GetByLogin(username);
            if (user.Password == CreateHash(user.Salt, password))
            {
                res = true;
            }
            return res;
        }
        
    }
    
}
